'''
Created on Oct 4, 2012

@author: Himanshu
'''
import DTW 
import numpy as np
import heapq as hp
class kNN():
    '''
    simple k nearest neighbor classifier. Training set is a dictionary of gesture objects with 
    keys being their names
    '''
        
    def __init__(self , parameters):
        '''
        Constructor
        '''
        self.trainingSet = None
        self.k = None
        self.parameters = parameters
    
    def setTraining(self , trainingSet):
        self.trainingSet = trainingSet
    
    def parameterToFeature(self , gest , parameter , i):
        return {
            'aNorm': gest.returnaNorm()[i] ,
            'aX': gest.returnaX()[i] ,
            'aY': gest.returnaY()[i] ,
            'aZ': gest.returnaZ()[i] ,
            'gX': gest.returngX()[i] ,
            'gY': gest.returngY()[i] ,
            'gZ': gest.returngZ()[i] ,
            'netaNorm' : gest.netaNorm[i]  ,
            'netaX' : gest.netaX[i] ,
            'netaY' : gest.netaY[i] ,
            'netaZ' : gest.netaZ[i] ,
            'worldaNorm' : gest.worldaNorm[i] ,
            'worldaX' : gest.worldaX[i] ,
            'worldaY' : gest.worldaY[i] ,
            'worldaZ' : gest.worldaZ[i] ,
        }[parameter]
    
    def setK(self, k):
        if (k==0):
            print 'invalid k, try again'
            
        self.k = k
        
    def classifyRestricted(self, query):
        """
        main classifier. Returns class of query matched against the training set. query is a single
        timeseries vector
        """
        h = []
        for gName in self.trainingSet.keys():
            gest = self.trainingSet[gName]
            score = gest.returnScore()
            noInstances = gest.returnaNorm().__len__()
            for i in range(noInstances):
                instance = []
                for parameter in self.parameters:
                    feature = self.parameterToFeature(gest , parameter , i)
                    instance = instance + feature;
                if (query.__len__() < instance.__len__()):
                    smallVector = query
                    largeVector = instance
                else:
                    smallVector = instance
                    largeVector = query
                lengthDiff = largeVector.__len__() - smallVector.__len__()
                minNormInstance = float('inf')
                for j in range(lengthDiff):
                    #norm = DTW.DTW(instance,query)
                    diff = np.subtract(smallVector, largeVector[j:(j + smallVector.__len__())])
                    norm = np.linalg.norm(diff, 2)
                    if (norm < minNormInstance):
                        minNormInstance = norm
                hp.heappush(h, (minNormInstance,score))
            
        
            
        #print minNorm
        return h
    
    def classifyAll(self, query):
        invNorms = []
        classes = []
        for gName in self.trainingSet.keys():
            gest = self.trainingSet[gName]
            score = gest.returnScore()
            for instance in gest.returnaNorm():
                if (query.__len__() < instance.__len__()):
                    smallVector = query
                    largeVector = instance
                else:
                    smallVector = instance
                    largeVector = query
                lengthDiff = largeVector.__len__() - smallVector.__len__()
                minNormInstance = float('inf')
                for j in range(lengthDiff):
                    #norm = DTW.DTW(instance,query)
                    diff = np.subtract(smallVector, largeVector[j:(j + smallVector.__len__())])
                    norm = np.linalg.norm(diff, 2)
                    if (norm < minNormInstance):
                        minNormInstance = norm
                invNorms.append(1.0/minNormInstance)
                classes.append(score)
#        invsq = []
#        for norm in invNorms:
#            invsq.append(norm**2)
#        #print minNorm
        return np.dot(invNorms,classes)/(np.sum(invNorms))
    